package com.playmodule.view;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;

import com.hg.highglass_library.R;
import com.playmodule.MediaUtil;
import com.playmodule.vr.utils.RawResourceReader;
import com.playmodule.vr.utils.ShaderHelper;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by wangjianqiang on 2016/9/23.
 */
public class VideoDisplayLeftRightView extends GLSurfaceView implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
    private boolean frameAvailable = false;
    int textureParamHandle;
    int textureCoordinateHandle;
    int positionHandle;
    int textureTranformHandle;

    /**
     *
     */
    private static float squareSize = 1.0f;
    private static float squareCoords[] = {
            -squareSize, squareSize,   // top left
            -squareSize, -squareSize,   // bottom left
            squareSize, -squareSize,    // bottom right
            squareSize, squareSize}; // top right

    private static short drawOrder[] = {0, 1, 2, 0, 2, 3};

    private Context context;

    // Texture to be shown in backgrund
    private FloatBuffer textureBuffer;
    private float textureCoords[] = {
            0.0f, 1.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 0.0f, 1.0f,
            1.0f, 0.0f, 0.0f, 1.0f,
            1.0f, 1.0f, 0.0f, 1.0f};
    private int[] textures = new int[1];

    private int width, height;

    private int shaderProgram;
    private FloatBuffer vertexBuffer;
    private ShortBuffer drawListBuffer;
    private float[] videoTextureTransform = new float[16];
    private SurfaceTexture videoTexture;
    public boolean hasCreated = false;
    private int mode = 0;//0 上下模式 1 左右倒置 2 上下左右倒置

    public VideoDisplayLeftRightView(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public VideoDisplayLeftRightView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    private void init() {
        setEGLContextClientVersion(2);
        setRenderer(this);
//        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        setupGraphics();
        setupVertexBuffer();
        setupTexture();
        hasCreated = true;
        try {
            Surface surface = new Surface(videoTexture);
            mediaHelper.getmMediaPlayer().setSurface(surface);
            mediaHelper.restart();
            surface.release();
        } catch (Exception e) {
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        synchronized (this) {
            if (frameAvailable) {
                videoTexture.updateTexImage();
                videoTexture.getTransformMatrix(videoTextureTransform);
                frameAvailable = false;
            }
        }
        this.drawTexture();

    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        synchronized (this) {
            frameAvailable = true;
        }
    }

    private void setupGraphics() {
        final String vertexShader = RawResourceReader.readTextFileFromRawResource(context, R.raw.vetext_sharder);
        final String fragmentShader = RawResourceReader.readTextFileFromRawResource(context, R.raw.fragment_sharder);

        final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
        final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
        shaderProgram = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
                new String[]{"texture", "vPosition", "vTexCoordinate", "textureTransform"});

        GLES20.glUseProgram(shaderProgram);
        textureParamHandle = GLES20.glGetUniformLocation(shaderProgram, "texture");
        textureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "vTexCoordinate");
        positionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
        textureTranformHandle = GLES20.glGetUniformLocation(shaderProgram, "textureTransform");
    }

    private void setupVertexBuffer() {
        // Draw list buffer
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        // Initialize the texture holder
        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());

        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);
    }

    private void setupTexture() {
        ByteBuffer texturebb = ByteBuffer.allocateDirect(textureCoords.length * 4);
        texturebb.order(ByteOrder.nativeOrder());

        textureBuffer = texturebb.asFloatBuffer();
        textureBuffer.put(textureCoords);
        textureBuffer.position(0);

        // Generate the actual texture
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glGenTextures(1, textures, 0);
        checkGlError("Texture generate");
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
        checkGlError("Texture bind");
        videoTexture = new SurfaceTexture(textures[0]);
        videoTexture.setOnFrameAvailableListener(this);
    }

    private void drawTexture() {
        // Draw texture
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(this.shaderProgram);
        checkGlError("glUseProgram");
        GLES20.glEnableVertexAttribArray(positionHandle);
        GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);

        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glUniform1i(textureParamHandle, 0);

        GLES20.glEnableVertexAttribArray(textureCoordinateHandle);
        GLES20.glVertexAttribPointer(textureCoordinateHandle, 4, GLES20.GL_FLOAT, false, 0, textureBuffer);

        GLES20.glUniformMatrix4fv(textureTranformHandle, 1, false, videoTextureTransform, 0);
        switch (mode) {
            case PlayView.VIDEO_MODE_UD:
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glViewport(0, -height, width / 2, height * 2);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glViewport(width / 2, 2, width / 2, height * 2);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                break;
            case PlayView.VIDEO_MODE_RL:
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glViewport(-width / 2, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glViewport(width / 2, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                break;
            case PlayView.VIDEO_MODE_RL_UD:
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glViewport(width / 2, -height, width / 2, height * 2);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
                GLES20.glViewport(0, 2, width / 2, height * 2);
                GLES20.glScissor(0, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
                break;
            case PlayView.VIDEO_MODE_4K:
                GLES20.glViewport(0, 0, width, height);
                GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
                GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
                break;
        }
        GLES20.glFinish();
    }

    public void checkGlError(String op) {
        int error;
        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e("SurfaceTest", op + ": glError " + GLUtils.getEGLErrorString(error));
        }
    }

    public void setMediaPlayer() {
        if (hasCreated) {
            Surface surface = new Surface(videoTexture);
            mediaHelper.getmMediaPlayer().setSurface(surface);
//            mediaHelper.restart();
            surface.release();
        }
    }

    public void setMediaHelper(MediaUtil mediaHelper) {
        this.mediaHelper = mediaHelper;
    }

    private MediaUtil mediaHelper;

    public void setMode(int Mode) {
        this.mode = Mode;
    }
}
